Skip to content

使用 Dapper 和 ODAC Managed Driver 無法寫入 Unicode 的問題

TLDR

  • 問題情境:使用 Dapper 搭配 Oracle.ManagedDataAccess.Core 寫入 Unicode 字元(如簡體中文)時,資料庫端出現亂碼。
  • 根本原因:ODP.NET 的驅動程式將 DbType.String 預設對應至 OracleDbType.Varchar2,而非支援 Unicode 的 OracleDbType.NVarchar2
  • 解決方案:無法直接透過 Dapper 的 DynamicParameters 設定 OracleDbType,需實作自訂的 IDynamicParameters 類別,手動將 OracleParameter 加入 IDbCommand

問題分析與成因

當開發者使用 Dapper 進行資料庫操作時,若未特別指定參數型別,Dapper 會依據 DbType 進行對應。在 Oracle 的 Managed Driver 中,DbType.String 被錯誤地映射為 OracleDbType.Varchar2,導致 Unicode 字元在傳輸過程中因編碼不匹配而產生亂碼。即使在程式碼中顯式指定 DbType.String 也無法解決此問題,因為驅動程式內部的對應邏輯並未將其導向 OracleDbType.NVarchar2

自訂 DynamicParameters 解決方案

由於 Dapper 原生的 DynamicParameters 不支援直接傳入 OracleParameter 物件以指定 OracleDbType,因此需要透過實作 SqlMapper.IDynamicParameters 介面來擴充功能,允許手動加入 IDbDataParameter

實作步驟

建立一個自訂的 MyDynamicParameters 類別,將參數轉發至 IDbCommand

csharp
public class MyDynamicParameters : SqlMapper.IDynamicParameters {
    private readonly Dapper.DynamicParameters dynamicParameters = new();
    private readonly List<IDbDataParameter> dbDataParameters = new();

    public void Add(string name, object value, DbType? dbType, ParameterDirection? direction, int? size) {
        dynamicParameters.Add(name, value, dbType, direction, size);
    }

    public void Add(IDbDataParameter paramerter) {
        dbDataParameters.Add(paramerter);
    }

    void SqlMapper.IDynamicParameters.AddParameters(IDbCommand command, SqlMapper.Identity identity) {
        ((SqlMapper.IDynamicParameters)dynamicParameters).AddParameters(command, identity);

        foreach (IDbDataParameter p in dbDataParameters) {
            command.Parameters.Add(p);
        }
    }
}

使用方式

在執行 SQL 指令時,改用 MyDynamicParameters 並明確指定 OracleDbType.NVarchar2

csharp
using (IDbConnection conn = new OracleConnection(connStr)) {
    conn.Open();

    MyDynamicParameters parameters = new();
    parameters.Add(new OracleParameter {
        ParameterName = "Name",
        Value = value,
        OracleDbType = OracleDbType.NVarchar2
    });
    conn.Query(sql, parameters);
}

透過此方式,即可確保參數以正確的 Unicode 格式寫入 Oracle 資料庫,避免亂碼問題。

異動歷程

    • 初版文件建立。